package main

import (
	"fmt"
	"time"
)

/*
1、go中哲学：不要用共享内存(全局变量)通信、而要通过通信来实现内存共享
2、其他语言的通信方式：
*、通过全局变量来进行两个线程之间的通信
*、消息队列机制、存在生产者和消费者(go中建议采用这种)
3、go提供channel和语法糖让消息队列的模式更加简单
4、channel的定义语法：var 通道名 chan 存放的类型  (其中chan为关键字，存放的类型代表channel中放的数据类型，channel底层是环形数组)
5、channel一管道是引用类型，必须初始化才能写入数据，即make实例化后才能使用，不实例化的时候默认值为nil，不实例化放不了值，缓冲区大小make时不写或为0代表为无缓冲channle,大于0为有缓冲channel
6、会发出deadlock的情况：1、waitgroup少调用了done时 2、 无缓冲的channel时
7、channel的放值和取值都是通过语法糖<- ，放值(channel<左边)：channel类型 <- 值，取值：变量 := <- channel类型(channel在<右边)
8、两种channel的使用常见场景
有缓冲channle: 适用于消费者和生产者之间的，一个不断产生，一个不断消费
无缓冲channel: 适用于通知类，如B需要第一时间收到A的通知
9、go中优先考虑channel的场景：消息传递、消息过滤、信号广播、事件订阅和广播、任务分发、结果汇总、并发控制、同步和异步
10、channel 操作一般有三种： 发送、接收、关闭
发送语法： [channel对象] <- [发送数据]
接收语法： [变量] := <- [channel对象] 或者 <- [channel对象]
关闭语法： close([channel对象])
使用内置函数close可以关闭管道，当管道关闭后，就不能再向管道写数据了，但是任然可以从该管道读取数据
11、注意：
在没有使用协程的情况下，如果管道的数据已经全部取出，那么再取就会阻塞
有缓冲的channel不能存放大于缓冲容量大小的数据个数
12、channel在类型上，可以分为两种：
1、双向channel：既能接收又能发送的channel(默认类型)
2、单向channel：只能发送或只能接收的channel，即单向channel可以为分为：
（1）只写channel
（2）只读channel
13、管道（channel）特质介绍
（1）管道本质就是一个数据结构-队列
（2）数据是先进先出
（3）自身线程安全，多协程访问时，不需要加锁，channel本身就是线程安全的
（4）管道有类型的，一个string的管道只能存放string类型数据
14、channel（通道）是一种用于在 goroutine 之间进行通信和同步的特殊数据结构。它可以看作是一条管道，可以在不同的goroutine之间传递数据。
使用通道，你可以在goroutine之间发送和接收值。通道提供了一种安全、同步的方式来共享数据。它确保在发送操作完成之前，接收操作会一直等待，
并且在接收操作完成之前，发送操作也会一直等待。这种同步机制可以有效地避免并发访问共享数据时出现的竞争条件和数据竞争。
Golang并发的核心哲学是不要通过共享内存进行通信。所以数据在不同协程中的传输都是通过拷贝的形式完成的。
*/
func main() {
	/*
		一、channel的声明和实例化
	*/
	// 1、有缓冲的channel的声明和实例化
	var msg1 chan string         //声明channel
	msg1 = make(chan string, 1)  //通过make实例化，1代表是缓冲区大小，大于0代表有缓冲，注意管道的数据个数不能超过缓存大小
	msg2 := make(chan string, 2) //通过make和语法糖实现声明并实例化，管道可以放两个string类型的值
	msg1 <- "a"                  // channel的放值
	msg2 <- "b"
	getMsg1 := <-msg1 // channel的取值
	getMsg2 := <-msg2
	fmt.Println(getMsg1, getMsg2)
	fmt.Printf("管道还未被取走的元素数量长度：%v,管道的容量是：%v\n", len(msg2), cap(msg2))
	// 2、无缓冲的cchannel的声明和实例化（缓冲区大小为0）
	//注意无缓冲的channel一定要先goroutine取值，然后再放值
	var msg3 chan string
	msg3 = make(chan string) //不写缓冲区大小，也是无缓冲channel，默认为0
	// getMsg3 := <- msg3       //无缓冲的channel需要用goroutine来进行取值不然会死锁
	//如上这种没有make直接放值，也是无缓冲的channel
	msg4 := make(chan string, 0) //像这样缓冲区大小为0，也是无缓冲channel
	// 对于无缓冲的channel如何还是像上面一样使用会发生死锁，无缓冲的channel需要用goroutine来进行取值值，作为入参
	go func(a, b chan string) {
		//通过goroutine来进行无缓冲channel的放值，go的appen-before的机制可以保障这里取值先于下面的放值，这样就不会发生死锁
		getMsg3 := <-a
		getMsg4 := <-b
		fmt.Println(getMsg3)
		fmt.Println(getMsg4)
	}(msg3, msg4)

	msg3 <- "无缓冲channel-msg3"
	msg4 <- "无缓冲channel-msg4"
	time.Sleep(2 * time.Second)

}
